package folder.main.exp2;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.math3.distribution.TDistribution;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;

import folder.main.exp2.algorithms.KCenter;
import folder.main.exp2.algorithms.NumberPaths;
import folder.main.exp2.algorithms.Algorithm2;
import folder.main.exp2.algorithms.MinKCenter;
import folder.main.exp2.algorithms.Random;
import folder.model.network.Network;
import folder.model.network.NetworkRandom;
import folder.model.node.Node;
import folder.model.virtual.VirtualNetwork;
import folder.model.virtual.VirtualNetworkRandom;

public class Exp2
{
	static String FILE_1_NAME = "C:/Users/aurelioj/Google Drive/R/Exp2/Nodes_1_ControllerNumber.txt";
	static String FILE_2_NAME = "C:/Users/aurelioj/Google Drive/R/Exp2/Nodes_2_DelayMeanControllerEmpty.txt";
	static String FILE_3_NAME = "C:/Users/aurelioj/Google Drive/R/Exp2/Nodes_3_DelayMeanControllerFull.txt";
	static String FILE_4_NAME = "C:/Users/aurelioj/Google Drive/R/Exp2/Nodes_4_DelayLongerControllerEmpty.txt";
	static String FILE_5_NAME = "C:/Users/aurelioj/Google Drive/R/Exp2/Nodes_5_DelayLongerControllerFull.txt";

	static int NUMBER_ALGORITHMS = 3;
	static int NETWORK_DEGREE = 5;
	static double DELAY_THRESHOLD = 0.000100; // 100 us
	
	static int NUMBER_EVALUATIONS = 1000;
	static int NUMBER_NODES = 95;
	static int NUMBER_VIRTUAL_NETWORKS_80 = 475;
	
	static SummaryStatistics sumDelayToNodeTargetMean = new SummaryStatistics();
	
	static Network network;
	static List< Algorithm2 > algorithms;
	static List< VirtualNetwork > virtualNetworks;	
	
	static List < SummaryStatistics > sumOccupation;
	
	static List < SummaryStatistics > sumNumberControllers;
	static List < SummaryStatistics > sumDelayMeanControllerEmpty;
	static List < SummaryStatistics > sumDelayMeanControllerFull;
	static List < SummaryStatistics > sumDelayLongerControllerEmpty;
	static List < SummaryStatistics > sumDelayLongerControllerFull;
	
	public static void main( String[] args )
	{
		setSummaryStatistics();
		setVirtualNetworks();
		
		int evaluationNumber = 0;
		
		while( evaluationNumber < NUMBER_EVALUATIONS )
		{
			int NUMBER_LINKS = NETWORK_DEGREE * ( NUMBER_NODES / 2 );
			network = NetworkRandom.networkRandom( NUMBER_NODES, NUMBER_LINKS );
			algorithms = runAlgorithms();
			
			if( algorithms != null )
			{
				setEvaluation();
				setSumDelayToNodeTargetMean( network );
				
				evaluationNumber++;
				
				System.out.println( evaluationNumber );
			}
		}
		
		printAndSaveToFile();
	}
	
	static void setSummaryStatistics()
	{
		sumOccupation = new ArrayList< SummaryStatistics >();
		
		sumNumberControllers = new ArrayList< SummaryStatistics >();
		sumDelayMeanControllerEmpty = new ArrayList< SummaryStatistics >();
		sumDelayMeanControllerFull = new ArrayList< SummaryStatistics >();
		sumDelayLongerControllerEmpty = new ArrayList< SummaryStatistics >();
		sumDelayLongerControllerFull = new ArrayList< SummaryStatistics >();
		
		for( int i = 0; i < NUMBER_ALGORITHMS; i++ )
		{
			sumOccupation.add( new SummaryStatistics() );
			
			sumNumberControllers.add( new SummaryStatistics() );
			sumDelayMeanControllerEmpty.add( new SummaryStatistics() );
			sumDelayMeanControllerFull.add( new SummaryStatistics() );
			sumDelayLongerControllerEmpty.add( new SummaryStatistics() );
			sumDelayLongerControllerFull.add( new SummaryStatistics() );
		}
	}
	
	static void setVirtualNetworks()
	{
		virtualNetworks = new ArrayList< VirtualNetwork >();
		
		for( int i = 0; i < NUMBER_VIRTUAL_NETWORKS_80; i++ )
		{
			VirtualNetwork virtualNetworkRandom = VirtualNetworkRandom.virtualNetworkRandom( i );
			virtualNetworks.add( virtualNetworkRandom );
		}
	}
	
	static List< Algorithm2 > runAlgorithms()
	{
		List< Algorithm2 > algorithms = new ArrayList< Algorithm2 >();
		
		Algorithm2 algorithm = NumberPaths.numberPaths( network, DELAY_THRESHOLD, virtualNetworks );
		
		if( algorithm.getCheckAlgorithm() )
			algorithms.add( algorithm );
		else
			return null;
		
		algorithm = MinKCenter.minKCenter( network, DELAY_THRESHOLD, virtualNetworks );

		if( algorithm.getCheckAlgorithm() )
			algorithms.add( algorithm );
		else
			return null;
		
		/*algorithm = KCenter.kCenter( network, DELAY_THRESHOLD, virtualNetworks );

		if( algorithm.getCheckAlgorithm() )
			algorithms.add( algorithm );
		else
			return null;*/
		
		algorithm = Random.random( network, DELAY_THRESHOLD, virtualNetworks );
		
		if( algorithm.getCheckAlgorithm() )
			algorithms.add( algorithm );
		else
			return null;
		
		return algorithms;
	}
	
	static void setEvaluation()
	{
		for( int j = 0; j < algorithms.size(); j++  )
		{
			Algorithm2 algorithm = algorithms.get( j );
			
			sumOccupation.get( j ).addValue( algorithm.getOccupation() );
			
			sumNumberControllers.get( j ).addValue( algorithm.getNumberControllers() );
			sumDelayMeanControllerEmpty.get( j ).addValue( algorithm.getDelayMeanControllerEmpty() );
			sumDelayMeanControllerFull.get( j ).addValue( algorithm.getDelayMeanControllerFull() );
			sumDelayLongerControllerEmpty.get( j ).addValue( algorithm.getDelayLongerMeanControllerEmpty() );
			sumDelayLongerControllerFull.get( j ).addValue( algorithm.getDelayLongerMeanControllerFull() );
		}
	}
	
	static void setSumDelayToNodeTargetMean( Network networkPath )
	{
		SummaryStatistics sum = new SummaryStatistics();
		
		for( Node node: networkPath.getNodes() )
			sum.addValue( node.getDelayToNodeTargetMean() );
		
		sumDelayToNodeTargetMean.addValue( sum.getMean() );
	}
	
	static void printAndSaveToFile()
	{
		DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols();
		decimalFormatSymbols.setDecimalSeparator( '.' );
		
		DecimalFormat decimalFormat = new DecimalFormat( "0.0", decimalFormatSymbols );
		
		double factor = 1000000;
		
		double delayToNodeTargetMean = sumDelayToNodeTargetMean.getMean() * factor;
		String stringDelayToNodeTargetMean = decimalFormat.format( delayToNodeTargetMean );
		System.out.println( "Mean delay to each node: " + stringDelayToNodeTargetMean );
		
		System.out.println( "Threshold delay: " + DELAY_THRESHOLD * factor );
		
		String dataToAddToFile1 = "";
		String dataToAddToFile2 = "";
		String dataToAddToFile3 = "";
		String dataToAddToFile4 = "";
		String dataToAddToFile5 = "";
		
		for( int i = 0; i < NUMBER_ALGORITHMS; i++  )
		{
			String strOccupation = getStringOfData( sumOccupation.get( i ), decimalFormat, 100 );
			
			String data1 = getStringOfData( sumNumberControllers.get( i ), decimalFormat, 1 );
			String data2 = getStringOfData( sumDelayMeanControllerEmpty.get( i ), decimalFormat, factor );
			String data3 = getStringOfData( sumDelayMeanControllerFull.get( i ), decimalFormat, factor );
			String data4 = getStringOfData( sumDelayLongerControllerEmpty.get( i ), decimalFormat, factor );
			String data5 = getStringOfData( sumDelayLongerControllerFull.get( i ), decimalFormat, factor );
			
			System.out.println( "\t\t\t\t\t\t" + algorithms.get( i ).getClass().getSimpleName() );
			System.out.println( "Percentage bandwidth occupation: \t\t" + strOccupation + "\n" );
			
			System.out.println( "Number of controllers: \t\t\t\t" + data1 + "\n" );
			
			System.out.println( "Mean delay from controller to normal empty: \t" + data2 );
			System.out.println( "Mean delay from controller to normal full: \t" + data3 + "\n" );
			
			System.out.println( "Longer delay from controller to normal empty: \t" + data4 );
			System.out.println( "LONGER DELAY FROM CONTROLLER TO NORMAL FULL: \t" + data5 + "\n" );
			
			dataToAddToFile1 += data1;
			dataToAddToFile2 += data2;
			dataToAddToFile3 += data3;
			dataToAddToFile4 += data4;
			dataToAddToFile5 += data5;
		}
		
		addDataToFile( dataToAddToFile1, FILE_1_NAME );
		addDataToFile( dataToAddToFile2, FILE_2_NAME );
		addDataToFile( dataToAddToFile3, FILE_3_NAME );
		addDataToFile( dataToAddToFile4, FILE_4_NAME );
		addDataToFile( dataToAddToFile5, FILE_5_NAME );
	}
	
	static void addDataToFile( String data, String fileName )
	{
		try {
			FileWriter fileWriter = new FileWriter( fileName, true );
			PrintWriter printWriter = new PrintWriter( fileWriter );
			printWriter.println( data );
			printWriter.close();
			
		} catch (IOException e) {e.printStackTrace();}
	}
	
	static String getStringOfData( SummaryStatistics sum, DecimalFormat decimalFormat, double factor )
	{
		double mean = sum.getMean();
		double width = getConfidenceIntervalWidth( sum, 0.05 );
		return ( decimalFormat.format( mean * factor ) ) + "\t" + decimalFormat.format( ( mean - width ) * factor ) + "\t" + decimalFormat.format( ( mean + width ) * factor ) + "\t";
	}
	
	static double getConfidenceIntervalWidth( SummaryStatistics sum, double significance ) //throws MathException
	{
		if( sum.getN() == 0 )
			return 0;
		
		long N = sum.getN() > 1 ? sum.getN() : 2;
				
		TDistribution tDist = new TDistribution( N - 1 );
		double a = tDist.inverseCumulativeProbability( 1.0 - significance / 2 );
		return a * sum.getStandardDeviation() / Math.sqrt( sum.getN() );
	}
	
}
